extern "C" {
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
}
#include <sstream>
#include <string>
#include "cgi.hpp"
#include "utils.hpp"
#include "logger.hpp"
#include "mime_type.h"

#define R (0)
#define W (1)

namespace cgi {

  // XXX
  static const char HTTP_V[] = "HTTP/1.1";
  static const char CRLF[] = "\r\n";
  static const char S200[] = "200 OK";
  static const char S500[] = "500 Internal Server Error";

  static const char* CGI_EXTS[] = {"rb", "py", NULL};

  bool is_cgi(const char* path) {
    const char* ext = util::get_ext(path);
    if (ext == NULL) {
      return false;
    }
    for (int i = 0; CGI_EXTS[i] != NULL; ++i) {
      if (strcmp(ext, CGI_EXTS[i]) == 0) {
        return true;
      }
    }
    return false;
  }

  bool run(Connection& conn) {
    int pipe_fds[2];
    if (pipe(pipe_fds) != 0) {
      logger::log("warn", __FILE__, __LINE__, "pipe error: %s", strerror(errno));
      return false;
    }
    const char* path = conn.get_req("PATH_TRANSLATED");
    // TODO prog, args, cgienv の準備
    char* argv[2] = {0};  // 末尾は NULL で終わる必要がある
    char prog[1024];
    memset(prog, 0, sizeof(prog));
    sprintf(prog, "%s", path);
    argv[0] = prog;
    char* cgienv[2] = {0};
    // cgienv[0] = request.at("Method").c_str();
    char method[128];
    memset(method, 0, sizeof(method));
    sprintf(method, "%s", conn.get_req("Method"));
    cgienv[0] = method;
    int ch_pid;
    switch (ch_pid = fork()) {
      case -1:
        logger::log("warn", __FILE__, __LINE__, "fork error: %s", strerror(errno));
        close(pipe_fds[R]);
        close(pipe_fds[W]);
        return false;
      case 0: {   // 子プロセス
        dup2(pipe_fds[W], 1); // pipe 書き込みを標準出力へ
        close(pipe_fds[R]);
        close(pipe_fds[W]);
        if (execve(path, argv, cgienv) < 0) {
          fprintf(stdout, "%s%s", CRLF, strerror(errno));
          exit(EXIT_FAILURE);  // 強制終了
        }
        // not reach
      }
      default: {  // 自身
        close(pipe_fds[W]);
        char buf[4096];
        std::ostringstream os;
        FILE* pipe_fh = fdopen(pipe_fds[R], "r");
        logger::log("info", __FILE__, __LINE__, "wait cgi-output...");
        char* error = NULL;
        while (fgets(buf, sizeof(buf), pipe_fh) != NULL) {
          os << buf;
          if (buf[0] == '\r' && buf[1] == '\n') {
            if (fgets(buf, sizeof(buf), pipe_fh) != NULL) {
              error = buf;
            }
            break;
          }
        }
        logger::log("info", __FILE__, __LINE__, "end cgi-output.");
        fclose(pipe_fh);
        close(pipe_fds[R]);
        if (error != NULL) {
          logger::log("warn", __FILE__, __LINE__, "cgi error: %s", error);
          conn.send_http_status(HTTP_V, S500);
          return false;
        }
        // 非 NPH
        const std::string& body_str = os.str();
        conn.set_res("Content-Type", get_mime_type(".html"));
        conn.set_res("Content-Length", body_str.size());
        conn.set_res("Last-Modified", util::time::get_date_str_rfc1123());
        conn.send_status_line(HTTP_V, S200);
        conn.send_header();
        conn.send(body_str.c_str(), body_str.size());
        //send_response_header_200(conn.c_socket(), conn.response());
        //send_all(conn.c_socket(), body_str.c_str(), body_str.size());
      }
    }
    // free_cgienv(cgienv);
    return true;
  }
//  static char** create_cgienv(const string_map& env, const string_map& request) {
//    int size = 2;
//    char** ret = new char*[size]; // 末尾は NULL
//    ret[0] = request.at("Method").c_str();
//    ret[size - 1] = NULL;
//    return ret;
//  }
}

